<?php
declare(strict_types=1);

/**
 * Passbolt ~ Open source password manager for teams
 * Copyright (c) Passbolt SA (https://www.passbolt.com)
 *
 * Licensed under GNU Affero General Public License version 3 of the or any later version.
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Passbolt SA (https://www.passbolt.com)
 * @license       https://opensource.org/licenses/AGPL-3.0 AGPL License
 * @link          https://www.passbolt.com Passbolt(tm)
 * @since         2.13.0
 */

namespace App\Service\Secrets;

use App\Error\Exception\CustomValidationException;
use App\Error\Exception\ValidationException;
use App\Model\Entity\Secret;
use App\Model\Table\SecretsTable;
use Cake\ORM\TableRegistry;

class SecretsCreateService
{
    /**
     * @var \App\Model\Table\SecretsTable
     */
    private SecretsTable $secretsTable;

    /**
     * Instantiate the service.
     */
    public function __construct()
    {
        $this->secretsTable = TableRegistry::getTableLocator()->get('Secrets');
    }

    /**
     * Create a secret.
     *
     * @param array $data The secret data
     * @param bool $checkRules Should run the table build rules. Default true.
     * @return \App\Model\Entity\Secret
     * @throws \Exception
     */
    public function create(array $data, bool $checkRules = true): Secret
    {
        $secret = $this->buildEntity($data);
        $this->handleValidationErrors($secret);

        $this->secretsTable->getConnection()->transactional(function () use ($secret, $checkRules): void {
            $this->secretsTable->save($secret, ['checkRules' => $checkRules]);
            $this->handleValidationErrors($secret);
        });

        return $secret;
    }

    /**
     * Build the entity.
     *
     * @param array $data The secret data
     * @return \App\Model\Entity\Secret
     */
    private function buildEntity(array $data): Secret
    {
        return $this->secretsTable->newEntity($data, ['accessibleFields' => [
            'resource_id' => true,
            'user_id' => true,
            'data' => true,
            'secret_revision_id' => true,
            'created_by' => true,
            'modified_by' => true,
        ]]);
    }

    /**
     * Create a secret.
     *
     * @param array $data The secret data
     * @return array<\App\Model\Entity\Secret>
     * @throws \Exception
     */
    public function createMany(array $data): array
    {
        $secrets = [];
        foreach ($data as $rowIndexRef => $row) {
            try {
                $secrets[$rowIndexRef] = $this->create($row);
            } catch (ValidationException $e) {
                $errors = [$rowIndexRef => $e->getEntity()->getErrors()];
                throw new CustomValidationException(__('Could not validate secrets data.'), $errors);
            }
        }

        return $secrets;
    }

    /**
     * Handle validation errors.
     *
     * @param \App\Model\Entity\Secret $secret The secret
     * @return void
     */
    private function handleValidationErrors(Secret $secret): void
    {
        $errors = $secret->getErrors();
        if (!empty($errors)) {
            throw new ValidationException(__('Could not validate secret data.'), $secret, $this->secretsTable);
        }
    }
}
